<?php

/***

LZRoster: A roster for World of Warcraft.
Copyright (C) 2007  Emmanuel Cron

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

***/

require_once dirname(__FILE__)."/Sql.class.php";
require_once dirname(__FILE__)."/Config.class.php";
require_once dirname(__FILE__)."/Localization.class.php";
require_once dirname(__FILE__)."/StatusRoster.class.php";
require_once dirname(__FILE__)."/SessionRoster.class.php";

class FileImporter
{
	private $upload_data;
	private $num_items_deleted = 0;
	private $num_spells_deleted = 0;
	private $num_items_updated = 0;
	private $num_spells_updated = 0;
	private $num_crafts_updated = array();
	private $num_reagents_updated = array();
	private $num_dungeons_updated = array();
	private $account_owns_character = null;
	
	static private $acc_own_not_created = 1;
	static private $acc_own_own = 2;
	static private $acc_own_no_own = 3;
	
	private $sql;
	private $config;
	private $localization;
	private $status_roster;
	private $session_roster;
	
	function FileImporter(&$file)
	{
		$this->upload_data = $file;
		
		$this->sql = Sql::GetInstance();
		$this->config = Config::GetInstance();
		$this->localization = Localization::GetInstance();
		$this->status_roster = StatusRoster::GetInstance();
		$this->session_roster = SessionRoster::GetInstance();
	}
	
	function Import()
	{
		if (
			!array_key_exists('Items', $this->upload_data)
			or
			!array_key_exists('Spells', $this->upload_data)
			or
			!array_key_exists('Characters', $this->upload_data)
			or
			!array_key_exists('Revision', $this->upload_data)
			or
			!array_key_exists('Locale', $this->upload_data)
		)
		{
			$this->status_roster->AddError($this->localization->Get('your file is not well formed, cannot continue'));
			return false;
		}
		
		if ( $this->upload_data['Revision'] < 58180 )
		{
			$this->status_roster->AddError($this->localization->Get('your version of LZProfiler is no more supported, please update your addon'));
			return false;
		}
		
		/*** Items ***/
		if ($this->DeleteOldItems() != true)
		{
			$this->status_roster->AddError($this->localization->Get('could not delete old items for update'));
			return false;
		}
		else
		{
			$this->status_roster->AddSuccess($this->localization->Get('deleted %s old items', $this->GetNumItemsDeleted()));
		}
		
		if ($this->ImportNewItems() != true)
		{
			$this->status_roster->AddError($this->localization->Get('could not import your collected items'));
			return false;
		}
		else
		{
			$this->status_roster->AddSuccess($this->localization->Get('updated %s items in the database', $this->GetNumItemsUpdated()));
		}
		
		/*** Spells ***/
		if ($this->DeleteOldSpells() != true)
		{
			$this->status_roster->AddError($this->localization->Get('could not delete old spells for update'));
			return false;
		}
		else
		{
			$this->status_roster->AddSuccess($this->localization->Get('deleted %s old spells', $this->GetNumSpellsDeleted()));
		}
		
		if ($this->ImportNewSpells() != true)
		{
			$this->status_roster->AddError($this->localization->Get('could not import your collected spells in the database'));
			return false;
		}
		else
		{
			$this->status_roster->AddSuccess($this->localization->Get('updated %s spells in the database', $this->GetNumSpellsUpdated()));
		}
		
		$this->status_roster->AddBlank();
		$this->status_roster->AddNote($this->localization->Get('now importing characters that are on %s server and in one of these guilds %t', $this->config->GetRosterServer(), implode(", ", $this->config->GetRosterGuilds())));
		$this->status_roster->AddBlank();
		
		/*** Characters ***/
		foreach ($this->GetDataCharacters() as $character_name)
		{
			$this->status_roster->AddNote($this->localization->Get('importing data for %s', $character_name));
			
			if ( $this->IsInAuthorizedGuild($character_name) == false )
			{
				$this->status_roster->AddError($this->localization->Get('this character is not member of an authorized guild'));
			}
			elseif ($this->AccountOwnsCharacter($character_name) == self::$acc_own_no_own)
			{
				$this->status_roster->AddError($this->localization->Get('this character is not owned by your account'));
			}
			else
			{
				if ( $this->ImportCharacter($character_name) == false)
				{
					$this->status_roster->AddError($this->localization->Get('update of character failed'));
				}
				else
				{
					$this->status_roster->AddSuccess($this->localization->Get('character successfully updated'));
					
					/*** Professions ***/
					if ( $this->DeleteOldProfessions($character_name) == false )
					{
						$this->status_roster->AddError($this->localization->Get('could not delete old professions'));
					}
					else
					{
						$this->status_roster->AddSuccess($this->localization->Get('deletion of old professions successful'));
						
						if ( $this->ImportNewProfessions($character_name) == true )
						{
							$this->status_roster->AddSuccess($this->localization->Get('updated %s crafts and %t reagents', $this->GetNumCraftsUpdated($character_name), $this->GetNumReagentsUpdated($character_name)));
						}
					}
					
					/*** Dungeons ***/
					if ( $this->ImportDungeons($character_name) == false )
					{
						$this->status_roster->AddError($this->localization->Get('update of dungeon access failed'));
					}
					else
					{
						$this->status_roster->AddSuccess($this->localization->Get('updated %s dungeon access', $this->GetNumDungeonsUpdated($character_name)));
					}
				}
				
			}
			
			$this->status_roster->AddBlank();
		}
		
		/*** Statistics ***/
		if ( $this->UpdateStatistics() == false )
		{
			$this->status_roster->AddError($this->localization->Get('could not update the statistics of your account'));
		}
		else
		{
			$this->status_roster->AddSuccess($this->localization->Get('statistics of your account updated successfully'));
		}
		
		$this->status_roster->AddBlank();
		$this->status_roster->AddNote($this->localization->Get('end of process'));
		
		return true;
	}
	
	private function UpdateStatistics()
	{
		if ($this->sql->Query("
			UPDATE
				`cp_accounts`
			SET
				`account_times_uploaded` = `account_times_uploaded`+1,
				`account_nb_items_updated` = `account_nb_items_updated`+".$this->GetNumItemsUpdated().",
				`account_nb_spells_updated` = `account_nb_spells_updated`+".$this->GetNumSpellsUpdated().",
				`account_nb_reagents_updated` = `account_nb_reagents_updated`+".$this->GetNumReagentsUpdated().",
				`account_last_upload` = NOW()
			WHERE
				`account_id`='".$this->session_roster->GetAccountId()."'
		") != true)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	
	private function AccountOwnsCharacter($character_name)
	{
		if ( $this->account_owns_character == null)
		{
			$this->account_owns_character = array();
			
			$this->sql->Query("
				SELECT
					`character_account_id`,
					`Name`
				FROM
					`cp_characters`
				WHERE
					`Name` IN ('".implode("', '", $this->GetDataCharacters())."')
			");
			
			while ( $v = $this->sql->IterateResults() )
			{
				$this->account_owns_character[$v['Name']] = $v['character_account_id'];
			}
		}
		
		if ( !array_key_exists($character_name, $this->account_owns_character) )
		{
			return self::$acc_own_not_created;
		}
		elseif ( $this->account_owns_character[$character_name] == $this->session_roster->GetAccountId() )
		{
			return self::$acc_own_own;
		}
		else
		{
			return self::$acc_own_no_own;
		}
	}
	
	private function DeleteOldItems()
	{
		if ( count($this->upload_data['Items']) < 1 )
		{
			return true;
		}
		
		if ($this->sql->Query("
			DELETE FROM
				`cp_items`
			WHERE
				`Revision` < '".$this->GetDataRevision()."'
			AND
				`Locale`='".$this->GetDataLocale()."'
			AND
				`ItemId` IN ('".implode("', '", $this->GetNewItemsIds())."')
		") != true)
		{
			return false;
		}
		else
		{
			$this->num_items_deleted = $this->num_items_deleted + $this->sql->GetResultsNumber();
			
			return true;
		}
	}
	
	private function ImportNewProfessions($character_name)
	{
		if ( count($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name]['Professions']) < 1 )
		{
			return true;
		}
		
		$this->GetProfessionsData_Sql($character_name, $professions, $reagents);
		
		if ($this->sql->Query("
			INSERT IGNORE INTO
				`cp_reagents`
			
				(
				`reagent_profession_custom_id`,
				`Revision`,
				`ItemId`,
				`ItemQuantity`
				)
			VALUES
				".$reagents."
		") != true)
		{
			$this->status_roster->AddError($this->localization->Get('could not update all reagents'));
			
			return false;
		}
		
		$this->num_reagents_updated[$character_name] = $this->sql->GetResultsNumber();
		
		if ($this->sql->Query("
			INSERT IGNORE INTO
				`cp_professions`
			
				(
				`profession_custom_id`,
				`Locale`,
				`CharacterName`,
				`ProfessionName`,
				`CategoryName`,
				`CraftName`,
				`ItemId`,
				`SpellId`
				)
			VALUES
				".$professions."
		") != true)
		{
			$this->status_roster->AddError($this->localization->Get('could not update all crafts'));
			
			return false;
		}
		
		$this->num_crafts_updated[$character_name] = $this->sql->GetResultsNumber();
		
		return true;
	}
	
	private function GetNumReagentsUpdated($character_name = null)
	{
		if ($character_name == null)
		{
			$t_reagents = 0;
			
			foreach($this->num_reagents_updated as $v)
			{
				$t_reagents = $t_reagents + $v;
			}
			
			return $t_reagents;
		}
		
		if ( array_key_exists($character_name, $this->num_reagents_updated) )
		{
			return $this->num_reagents_updated[$character_name];
		}
		else
		{
			return 0;
		}
	}
	
	private function GetNumCraftsUpdated($character_name)
	{
		if ( array_key_exists($character_name, $this->num_crafts_updated) )
		{
			return $this->num_crafts_updated[$character_name];
		}
		else
		{
			return 0;
		}
	}
	
	private function ImportNewItems()
	{
		if ( count( $this->upload_data['Items'] ) < 1 )
		{
			return true;
		}
		
		if ($this->sql->Query("
			INSERT IGNORE INTO
				`cp_items`
			
				(
				`Revision`,
				`Locale`,
				`ItemId`,
				`Name`,
				`Color`,
				`TooltipLeft`,
				`TooltipRight`,
				`Texture`
				)
			VALUES
				".$this->GetObjectData_Sql('Items')."
		") != true)
		{
			return false;
		}
		else
		{
			$this->num_items_updated = $this->num_items_updated + $this->sql->GetResultsNumber();
			return true;
		}
	}
	
	private function GetNewItemsIds()
	{
		$table = array();
		
		foreach ($this->upload_data['Items'] as $k => $v)
		{
			array_push($table, $k);
		}
		
		return $table;
	}
	
	private function DeleteOldSpells()
	{
		if ( count($this->upload_data['Spells']) < 1 )
		{
			return true;
		}
		
		if ($this->sql->Query("
			DELETE FROM
				`cp_spells`
			WHERE
				`Revision` < '".$this->GetDataRevision()."'
			AND
				`Locale`='".$this->GetDataLocale()."'
			AND
				`SpellId` IN ('".implode("', '", $this->GetNewSpellsIds())."')
		") != true)
		{
			return false;
		}
		else
		{
			$this->num_spells_deleted = $this->num_spells_deleted + $this->sql->GetResultsNumber();
			
			return true;
		}
	}
	
	private function ImportNewSpells()
	{
		if ( count( $this->upload_data['Spells'] ) < 1 )
		{
			return true;
		}
		
		if ($this->sql->Query("
			INSERT IGNORE INTO
				`cp_spells`
			
				(
				`Revision`,
				`Locale`,
				`SpellId`,
				`Name`,
				`Color`,
				`TooltipLeft`,
				`TooltipRight`,
				`Texture`
				)
			VALUES
				".$this->GetObjectData_Sql('Spells')."
		") != true)
		{
			return false;
		}
		else
		{
			$this->num_spells_updated = $this->num_spells_updated + $this->sql->GetResultsNumber();
			return true;
		}
	}
	
	private function ImportDungeons($character_name)
	{
		if ( count($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name]['Dungeons']) < 1 )
		{
			return true;
		}
		
		if ($this->sql->Query("
			INSERT IGNORE INTO
				`cp_dungeons`
			
				(
				`CharacterName`,
				`Dungeon`
				)
			VALUES
				".$this->GetDungeonData_Sql($character_name)."
		") != true)
		{
			return false;
		}
		else
		{
			$this->num_dungeons_updated[$character_name] = $this->sql->GetResultsNumber();
			
			return true;
		}
	}
	
	private function ImportCharacter($character_name)
	{
		$word = null;
		$where = null;
		
		if ( $this->AccountOwnsCharacter($character_name) == self::$acc_own_not_created )
		{
			$word = "INSERT INTO";
		}
		else
		{
			$word = "UPDATE";
			$where = "WHERE `Name` = '".$character_name."'";
		}
		
		$root = $this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name];
		
		if ($this->sql->Query("
			".$word."
				`cp_characters`
			SET
				`character_account_id` = '".$this->session_roster->GetAccountId()."',
				`Revision` = '".$this->GetDataRevision()."',
				`Locale` = '".$this->GetDataLocale()."',
				`Name` = '".$character_name."',
				`FactionEn` = '".$root['FactionEn']."',
				`RaceEn` = '".$root['RaceEn']."',
				`ClassEn` = '".$root['ClassEn']."',
				`SexId` = '".$root['SexId']."',
				`Level` = '".$root['Level']."',
				`CurrentExperience` = '".$root['CurrentExperience']."',
				`MaxExperience` = '".$root['MaxExperience']."',
				`RestedExperience` = ".( array_key_exists('RestedExperience', $root) ? "'".$root['RestedExperience']."'" : "NULL" ).",
				`GuildTitle` = ".( array_key_exists('Guild', $root) ? "'".$root['Guild']['Title']."'" : "NULL" ).",
				`GuildRank` = ".( array_key_exists('Guild', $root) ? "'".$root['Guild']['Rank']."'" : "NULL" ).",
				`TimePlayed` = '".$root['TimePlayed']."',
				`TimeLevelPlayed` = '".$root['TimeLevelPlayed']."',
				`timestampUpdate` = '".$root['timestampUpdate']."'
			".$where."
		") != true)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	
	private function GetDungeonData_Sql($character_name)
	{
		$string = null;
		
		foreach ($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name]['Dungeons'] as $k => $v)
		{
			$string .= "(";
			$string .= "'".$character_name."'";
			$string .= ",";
			$string .= "'".$k."'";
			$string .= ")";
			$string .= ",";
		}
		
		$string = substr($string, 0, strlen($string)-1);
		
		return $string;
	}
	
	private function GetProfessionsData_Sql($character_name, &$string_professions, &$string_reagents)
	{
		foreach ($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name]['Professions'] as $profession_name => $profession)
		{
			foreach ($profession as $category_or_enchant_name => $category_or_enchant)
			{
				if ( array_key_exists('Reagents', $category_or_enchant) )
				{
					$t_custom_id = $this->CreateCustomId($category_or_enchant); // Cache to avoid too much processing
					
					$string_professions .= $this->GetCraftData_Sql(
						$t_custom_id,
						$category_or_enchant,
						$character_name,
						$profession_name,
						null,
						$category_or_enchant_name
					);
					
					if ( count($category_or_enchant['Reagents']) > 0 )
					{
						foreach ($category_or_enchant['Reagents'] as $reagent_id => $reagent_data)
						{
							$string_reagents .= $this->GetReagentData_Sql($t_custom_id, $reagent_id, $reagent_data);
						}
					}
				}
				else
				{
					foreach ($category_or_enchant as $item_name => $item_data)
					{
						$t_custom_id = $this->CreateCustomId($item_data); // Cache to avoid too much processing
						
						$string_professions .= $this->GetCraftData_Sql(
							$t_custom_id,
							$item_data,
							$character_name,
							$profession_name,
							$category_or_enchant_name,
							$item_name
						);
						
						if ( count($item_data['Reagents']) > 0 )
						{
							foreach ($item_data['Reagents'] as $reagent_id => $reagent_data)
							{
								$string_reagents .= $this->GetReagentData_Sql($t_custom_id, $reagent_id, $reagent_data);
							}
						}
					}
				}
			}
		}
		
		$string_professions = substr($string_professions, 0, strlen($string_professions)-1);
		$string_reagents = substr($string_reagents, 0, strlen($string_reagents)-1);
	}
	
	private function GetReagentData_Sql($t_custom_id, $reagent_id, $reagent_data)
	{
		$string = null;
		
		$string .= "(";
		$string .= "'".$t_custom_id."'";
		$string .= ",";
		$string .= "'".$this->GetDataRevision()."'";
		$string .= ",";
		$string .= "'".$reagent_id."'";
		$string .= ",";
		$string .= "'".$reagent_data['Quantity']."'";
		$string .= ")";
		$string .= ",";
		
		return $string;
	}
	
	private function GetCraftData_Sql($t_custom_id, $craft_data, $character_name, $profession, $category, $craft_name)
	{
		$string = null;
		
		$string .= "(";
		$string .= "'".$t_custom_id."'";
		$string .= ",";
		$string .= "'".$this->GetDataLocale()."'";
		$string .= ",";
		$string .= "'".$character_name."'";
		$string .= ",";
		$string .= "'".$profession."'";
		$string .= ",";
		$string .= "'".$category."'";
		$string .= ",";
		$string .= "'".$craft_name."'";
		$string .= ",";
		$string .= "'".( array_key_exists('Item', $craft_data) ? $craft_data['Item'] : null )."'";
		$string .= ",";
		$string .= "'".( array_key_exists('Spell', $craft_data) ? $craft_data['Spell'] : null )."'";
		$string .= ")";
		$string .= ",";
		
		return $string;
	}
	
	private function GetObjectData_Sql($key)
	{
		$string = null;
		
		foreach ($this->upload_data[$key] as $k => $v)
		{
			$string .= "(";
			$string .= "'".$this->GetDataRevision()."'";
			$string .= ",";
			$string .= "'".$this->GetDataLocale()."'";
			$string .= ",";
			$string .= "'".$k."'";
			$string .= ",";
			$string .= "'".$v['RealName']."'";
			$string .= ",";
			$string .= "'".$v['Color']."'";
			$string .= ",";
			$string .= "'".implode("_\$_", $v['Tooltip']['left'])."'";
			$string .= ",";
			
			$string .= "'";
			if (array_key_exists('right', $v['Tooltip']))
			{
				$t_table = $v['Tooltip']['right'];
				
				if ( max( array_keys($v['Tooltip']['right']) ) > 1 )
				{
					$t_table = array_merge(
						array_fill(
							1,
							max(array_keys($v['Tooltip']['right']))-1,
							""
						),
						$v['Tooltip']['right']
					);
				}
				
				$string .= implode("_\$_", $t_table);
			}
			$string .= "'";
			$string .= ",";
			
			$string .= "'".$v['Texture']."'";
			$string .= ")";
			$string .= ",";
		}
		
		$string = substr($string, 0, strlen($string)-1);
		
		return $string;
	}
	
	private function DeleteOldProfessions($character_name)
	{
		if ( count($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name]['Professions']) < 1 )
		{
			return true;
		}
		
		$t_new_professions_ids = $this->GetNewProfessionsIds($character_name); // Cache to avoid too much processing
		
		if ($this->sql->Query("
			DELETE FROM
				`cp_professions`
			WHERE
				`CharacterName`='".$character_name."'
			AND
				`profession_custom_id` IN ('".implode("','", $t_new_professions_ids)."')
		") != true)
		{
			return false;
		}
		
		if ($this->sql->Query("
			DELETE FROM
				`cp_reagents`
			WHERE
				`Revision` < '".$this->GetDataRevision()."'
			AND
				`reagent_profession_custom_id` IN ('".implode("','", $t_new_professions_ids)."')
		") != true)
		{
			return false;
		}
		
		return true;
	}
	
	private function GetNewProfessionsIds($character_name)
	{
		$table = array();
		
		foreach ($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name]['Professions'] as $profession)
		{
			foreach ($profession as $category_or_enchant)
			{
				if ( array_key_exists('Reagents', $category_or_enchant) )
				{
					array_push($table, $this->CreateCustomId($category_or_enchant));
				}
				else
				{
					foreach ($category_or_enchant as $item_data)
					{
						array_push($table, $this->CreateCustomId($item_data));
					}
				}
			}
		}
		
		return $table;
	}
	
	private function CreateCustomId($object)
	{
		$custom_id = null;
		
		if ( array_key_exists('Item', $object) )
		{
			$custom_id .= "i".$object['Item'];
		}
		else
		{
			$custom_id .= "s".$object['Spell'];
		}
		
		if ( array_key_exists('Reagents', $object) and count($object['Reagents'] > 0) )
		{
			ksort($object['Reagents']);
			
			$custom_id .= ":";
			
			foreach ($object['Reagents'] as $k => $v)
			{
				$custom_id .= $k;
				$custom_id .= ",";
				$custom_id .= $v['Quantity'];
				$custom_id .= ":";
			}
			
			$custom_id = substr($custom_id, 0, strlen($custom_id)-1);
		}
		
		return $custom_id;
	}
	
	private function GetNewSpellsIds()
	{
		$table = array();
		
		foreach ($this->upload_data['Spells'] as $k => $v)
		{
			array_push($table, $k);
		}
		
		return $table;
	}
	
	private function GetDataRevision()
	{
		return $this->upload_data['Revision'];
	}
	
	private function GetDataLocale()
	{
		return $this->upload_data['Locale'];
	}
	
	private function GetNumItemsUpdated()
	{
		return $this->num_items_updated;
	}
	
	private function GetNumSpellsUpdated()
	{
		return $this->num_spells_updated;
	}
	
	private function GetNumSpellsDeleted()
	{
		return $this->num_spells_deleted;
	}
	
	private function GetNumItemsDeleted()
	{
		return $this->num_items_deleted;
	}
	
	private function GetNumDungeonsUpdated($character_name)
	{
		if ( array_key_exists($character_name, $this->num_dungeons_updated) )
		{
			return $this->num_dungeons_updated[$character_name];
		}
		else
		{
			return 0;
		}
	}
	
	private function IsInAuthorizedGuild($character_name)
	{
		$root = $this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()][$character_name];
		
		if ( !array_key_exists('Guild', $root) )
		{
			return false;
		}
		
		foreach ($this->config->GetRosterGuildsRawWithSlashes() as $guild_name)
		{
			if ( $root['Guild']['Name'] == $guild_name )
			{
				return true;
			}
		}
		
		return false;
	}
	
	private function GetDataCharacters()
	{
		if ( array_key_exists( $this->config->GetRosterServerRawWithSlashes(), $this->upload_data['Characters'] ) )
		{
			return array_keys($this->upload_data['Characters'][$this->config->GetRosterServerRawWithSlashes()]);
		}
		else
		{
			$this->status_roster->AddError($this->localization->Get('no character found on this server'));
			$this->status_roster->AddBlank();
			
			return array();
		}
	}
}

?>